#ifndef SOFTI2CPLUS_H_
#define SOFTI2CPLUS_H_

#include <inttypes.h>
#include <Stream.h>

#define BUFFER_LENGTH 32

class SoftI2CPlus : public Stream
{
private:
    // SDA和SCL引脚
    uint8_t SCL_pin, SDA_pin;
    uint8_t SCL_bitMask;
    uint8_t SDA_bitMask;
    volatile uint8_t *SCL_portReg;
    volatile uint8_t *SDA_portReg;
    volatile uint8_t *SCL_dirReg;
    volatile uint8_t *SDA_dirReg;
    uint8_t usePullups;
    // 设置SCL和SDA
    void setPins(uint8_t sclPin, uint8_t sdaPin, uint8_t usePullups);
    // 接收缓冲区，rx是接收(receive)
    uint8_t rxBuffer[BUFFER_LENGTH];
    uint8_t rxBufferIndex;
    uint8_t rxBufferLength;
    // 指明是否在传输
    uint8_t transmitting;
    // 错误类型
    uint8_t error;
    // I2C初始化
    void i2c_init(void);
    void i2c_start(void);
    void i2c_restart(void);
    void i2c_stop(void);
    void i2c_writebit(uint8_t data);
    uint8_t i2c_readbit(void);
    uint8_t i2c_write(uint8_t data);
    uint8_t i2c_read(uint8_t ack);
public:
    // 构造函数
    SoftI2CPlus();
    SoftI2CPlus(uint8_t sclPin, uint8_t sdaPin);
    SoftI2CPlus(uint8_t sclPin, uint8_t sdaPin, uint8_t pullups);
    // 设置引脚并初始化
    void setPinsInit(uint8_t sclPin, uint8_t sdaPin, uint8_t pullups);
    // 开始
    void begin(void);
    // 结束
    void end(void);
    // 设置时钟频率
    void setClock(uint32_t clock);
    void beginTransmission(uint8_t address);
    void beginTransmission(int address);
    uint8_t endTransmission(uint8_t sendStop);
    uint8_t endTransmission(void);
    size_t write(uint8_t data);
    size_t write(const uint8_t *data, size_t quantity);
    uint8_t requestFrom(uint8_t address, uint8_t quantity, uint32_t iaddress, uint8_t isize, uint8_t sendStop);
    uint8_t requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop);
    uint8_t requestFrom(int address, int quantity, int sendStop);
    uint8_t requestFrom(uint8_t address, uint8_t quantity);
    uint8_t requestFrom(int address, int quantity);
    int available(void);
    int read(void);
    int peek(void);
    void flush(void);
    inline size_t write(unsigned long n) { return write((uint8_t)n); }
    inline size_t write(long n) { return write((uint8_t)n); }
    inline size_t write(unsigned int n) { return write((uint8_t)n); }
    inline size_t write(int n) { return write((uint8_t)n); }
    inline size_t write(char* n) { return write((uint8_t *)n, strlen(n)); }
    using Print::write;
};

#endif